﻿//TODO: Have all of the Visual Studio snippets create virtual methods
//===================================================================================
// Copyright (c) Ryan Garrett.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===================================================================================
//TODO: Add ability for command to list reasons it is not enabled. The list should be filled out after the CanExecuteCall

using System;
using Caffeine.Core;
using System.Reflection;

namespace Caffeine.Core
{
    /// <summary>
    /// Delegate used to point to can-execute methods of model commands
    /// </summary>
    /// <typeparam name="T">Command parameter type</typeparam>
    /// <param name="parent">Parent of command</param>
    /// <param name="parameter">Parameter to consider</param>
    /// <returns>True if the command can execute with the given parameter, otherwise false</returns>
    public delegate Boolean CanExecuteCommandDel<T>(ModelBase parent, T parameter);
    
    /// <summary>
    /// Allows you to use a Action&lt;Object&gt; to implement ICommand
    /// </summary>
    public sealed class ModelCommand<T> : ModelCommandBase
    {
        #region State

        private readonly Action<ModelBase, T> _executeAction;
        private readonly CanExecuteCommandDel<T> _canExecutePredicate;

        #endregion

        #region Constructors

        internal ModelCommand
        (
            ModelBase parent, 
            Action<ModelBase, T> executeAction, 
            CanExecuteCommandDel<T> canExecutePredicate
        ) : base(parent)
        {
            if (executeAction == null)
            {
                throw new ArgumentNullException("executeAction");
            }

            if (canExecutePredicate == null)
            {
                throw new ArgumentNullException("canExecutePredicate");
            }

            TypeInfo parameterTypeInfo = typeof(T).GetTypeInfo();

            if (parameterTypeInfo.IsValueType && !parameterTypeInfo.IsGenericType)
            {
                CanExecuteResultOverrideForNulls = false;
            }

            _executeAction = executeAction;
            _canExecutePredicate = canExecutePredicate;
        }

        #endregion

        #region Public

        /// <summary>
        /// Gets the action that will be invoked when this command is executed
        /// </summary>
        public Action<ModelBase, T> ExecuteAction
        {
            get
            {
                return _executeAction;
            }
        }

        /// <summary>
        /// Gets the predicate used to determine whether or not this command can execute
        /// </summary>
        public CanExecuteCommandDel<T> CanExecutePredicate
        {
            get
            {
                return _canExecutePredicate;
            }
        }

        #endregion

        #region Protected

        protected override Boolean OnCanExecute(Object parameter)
        {
            Boolean returnResult = true;

            if (parameter != null)
            {
                returnResult = CanExecutePredicate(Parent, (T)parameter);
            }
            else
            {
                returnResult = CanExecuteResultOverrideForNulls ?? CanExecutePredicate(Parent, (T)parameter);
            }

            return returnResult;
        }

        protected override void OnExecute(Object parameter)
        {
            ExecuteAction(Parent, (T)parameter);
        }

        #endregion
    }
}
